#!/bin/bash

help() {
  echo "Runs the unit, env, or multiverse tests"
  echo
  echo "Syntax: run_tests [-u|e|m|q] arg1 arg2 arg3"
  echo "  options:"
  echo "  -u    Runs unit tests"
  echo "          optional arg 1: file_path OR test_name"
  echo "          optional arg 2: test_name (if also passing in file_path in arg 1)"
  echo "          Examples: "
  echo "            ./test/script/run_tests -u"
  echo "            ./test/script/run_tests -u test_name"
  echo "            ./test/script/run_tests -u path/to/test_file.rb"
  echo "            ./test/script/run_tests -u path/to/test_file.rb test_name"
  echo "        "
  echo "  -e    Runs env tests"
  echo "          arg 1: rails version to test. The leading 'rails' can be left off. 61,70 works like rails61,rails70"
  echo "          optional arg 2: file_path OR test_name"
  echo "          optional arg 3: test_name (if also passing in file_path in arg 2)"
  echo "          Examples: "
  echo "            ./test/script/run_tests -e 61"
  echo "            ./test/script/run_tests -e 61,71 test_name"
  echo "            ./test/script/run_tests -e rails61 path/to/test_file.rb"
  echo "            ./test/script/run_tests -e rails61,rails71 path/to/test_file.rb test_name"
  echo "        "
  echo "  -m    Runs multiverse tests"
  echo "          arg 1: multiverse suite to run (or any other args you wanna pass in to multiverse)"
  echo "            the 'debug' multiverse arg is automatically included"
  echo "          optional arg 2: file_path OR test_name"
  echo "          optional arg 3: test_name (if also passing in file_path in arg 2)"
  echo "          Examples: "
  echo "            ./test/script/run_tests -m rake"
  echo "            ./test/script/run_tests -m rake,env=2"
  echo "            ./test/script/run_tests -m rake test_name"
  echo "            ./test/script/run_tests -m rake path/to/test_file.rb"
  echo "            ./test/script/run_tests -m rake path/to/test_file.rb test_name"
  echo "        "
  echo "  -q    Runs quick multiverse tests. Prepend method only and env 0, unless env number is passed in"
  echo "          arg 1: multiverse suite to run (or any other args you wanna pass in to multiverse)"
  echo "            the 'debug' multiverse arg is automatically included"
  echo "          optional arg 2: env# OR file_path OR test_name"
  echo "          optional arg 3: file_path OR test_name (if also passing in env # in arg 2)"
  echo "          optional arg 4: test_name (if also passing in file_path in arg 3)"
  echo "          Examples: "
  echo "            ./test/script/run_tests -m rake"
  echo "            ./test/script/run_tests -m rake 2"
  echo "            ./test/script/run_tests -m rake test_name"
  echo "            ./test/script/run_tests -m rake path/to/test_file.rb"
  echo "            ./test/script/run_tests -m rake path/to/test_file.rb test_name"
  echo "        "
}

unit_command() {
  # echo "ENV   TEST="$TEST"    TESTOPTS="$TESTOPTS""
  # echo "bundle exec rake test -q;"
  bundle exec rake test -q;
}

env_test_command() { 
  # echo "ENV   TEST="$TEST"    TESTOPTS="$TESTOPTS""
  # echo "bundle exec rake test:env["$@"];"
  bundle exec rake test:env["$@"];
  }

multiverse_command() {
  # echo "ENV   TEST="$TEST"    TESTOPTS="$TESTOPTS""
  # echo "bundle exec rake test:multiverse["$@",debug];"
  bundle exec rake test:multiverse["$@",debug];
}

# doesn't overwrite testopts that already exist, 
# that way you can still pass in a seed if you want
set_test_opts() {
  test_opts_result=""$TESTOPTS" --name="$1""
}

# organizes the args for mutiverse and calls the command
run_multiverse() {  
  clean

  if [[ -n "$3" ]]; then
    # echo "running file and name"
    multiverse_command "$1",file="$2",name="$3"; 
  elif [[ -n "$2"  && "$2" =~ ^test_ ]]; then
    # echo "running name"
    multiverse_command "$1",name="$2"; 
  elif [[ -n "$2" ]]; then
    # echo "running file"
    multiverse_command "$1",file="$2"; 
  else
    # echo "running suite $1" 
    multiverse_command "$1"; 
  fi
}

# calls multiverse but only env 0 or specified and method prepend
run_multiverse_quick() {
  if [[ "$2" =~ [0-9]+ && ! "$2" =~ ^test ]]; then
    run_multiverse "$1",env="$2",method=prepend "$3" "$4"
  else
    run_multiverse "$1",env=0,method=prepend "$2" "$3"
  fi
}

# organizes the args for env tests and calls the command
run_env_tests() {
  clean

  # this will add "rails" before any number so you can pass in just "61" or "61,70"
  ENVARGS=$(echo "$1" | sed -E -e 's/(\,)|(\,rails)/,rails/g' | sed '/^rails/!s/^/rails/');

  if [[ -n "$3" ]]; then
    # echo "running file and name"
    set_test_opts $3
    TEST="../../../$2" TESTOPTS="$test_opts_result" env_test_command "$ENVARGS";
  elif [[ -n "$2"  && "$2" =~ ^test_ ]]; then
    # echo "running name"
    set_test_opts $2
    TESTOPTS="$test_opts_result" env_test_command "$ENVARGS";
  elif [[ -n "$2" ]]; then
    # echo "running file"
    TEST="../../../$2" env_test_command "$ENVARGS";
  else
    # echo "running env tests"
    env_test_command "$ENVARGS";
  fi
}

# organizes the args for the unit tests and calls the command
run_unit_tests() {
  clean

  find_test_file "$1" "new_relic"
  if [[ -n "$2" ]]; then
    # echo "running file and name"
    set_test_opts $2
    TEST=${TEST:-"$1"} TESTOPTS="$test_opts_result" unit_command;
  elif [[ -n "$1"  && "$1" =~ ^test_ ]]; then
    # echo "running name"
    set_test_opts $1
    TESTOPTS="$test_opts_result" unit_command;
  elif [[ -n "$1" ]]; then
    # echo "running file"
    TEST=${TEST:-"$1"} unit_command;
  else
    # echo "running unit tests"
    unit_command;
  fi
}

clean() {
  echo 'Cleaning...'
  # xargs over -exec to avoid warnings on already-deleted content
  find . -name \*.log | xargs rm -f
  find . -name tmp -type d | xargs rm -rf
  rm -rf lib/coverage test/minitest/minitest_time_report test/multiverse/lib/multiverse/errors.txt
  echo 'Done.'
}

# from a given space/newline delimited string of file paths, return the
# shortest path
#
# so if "bert agent" is invoked and all of these files match 'agent":
#
# new_relic/agent/rpm_agent_test.rb
# new_relic/agent/agent_test.rb
# new_relic/agent/distributed_tracing/trace_context_cross_agent_test.rb
# new_relic/agent/distributed_tracing/distributed_tracing_cross_agent_test.rb
# new_relic/agent/commands/agent_command_router_test.rb
# new_relic/agent/commands/agent_command_test.rb
# new_relic/agent/threading/agent_thread_test.rb
# new_relic/agent/agent_logger_test.rb
# new_relic/agent_test.rb
# new_relic/rack/agent_hooks_test.rb
# new_relic/rack/agent_middleware_test.rb
#
# then favor new_relic/agent_test.rb
shortest_test_file() {
  files="$1"
  shortest=1138000
  desired=""
  for file in $files; do
    len=${#file}
    if (( "$len" < "$shortest" )); then
      shortest=$len
      desired=$file
    fi
  done
  printf $desired
}

# If the first argument doesn't contain a slash or start with test_
# then assume it is a partial filename match. Find the file.
find_test_file() {
  if [[ "$1" != */* && "$1" != test* ]]; then
    files=$(find "test/$2" -type f -name "*$1*" -print)
    if [[ "$files" == "" ]]; then
      echo "Could not find a file match for '$1'"
      exit
    else
      file=$(shortest_test_file "$files")
      echo "Testing against file '$file'..."
      TEST="$file"
    fi
  fi
}

# helper method to output the help info and error and then exit the script
output_arg_error(){
  help
  echo $1
  exit
}

while getopts "huemq" option; do
  shift 1 # removes the option (ex: -u) from the args so we can pass all the args with $@
  case $option in
    h) 
      help
      exit;;

    u)
      run_unit_tests $@
      exit;;

    e)
      if ! [[ -n "$1" ]]; then
        output_arg_error "ERROR: Must specify at least one rails version"
      fi
      run_env_tests $@
      exit;;

    m)
      if ! [[ -n "$1" ]]; then
        output_arg_error "ERROR: Must specify multiverse suite"
      fi
      run_multiverse $@
      exit;;

    q)
      if ! [[ -n "$1" ]]; then
        output_arg_error "ERROR: Must specify multiverse suite"
      fi
      run_multiverse_quick $@
      exit;;
  esac
done

help
